// Copyright Epic Games, Inc. All Rights Reserved.

#include "Input/AimAssistInputModifier.h" // 包含瞄准辅助输入修改器头文件
#include "CommonInputTypeEnum.h" // 通用输入类型枚举
#include "Curves/CurveFloat.h" // 浮点曲线
#include "Engine/Engine.h" // 引擎
#include "Engine/GameViewportClient.h" // 游戏视口客户端
#include "Engine/World.h" // 世界
#include "EnhancedPlayerInput.h" // 增强玩家输入
#include "Input/AimAssistTargetManagerComponent.h" // 瞄准辅助目标管理器组件
#include "Input/LyraAimSensitivityData.h" // Lyra瞄准灵敏度数据
#include "Player/LyraLocalPlayer.h" // Lyra本地玩家
#include "Player/LyraPlayerState.h" // Lyra玩家状态
#include "SceneView.h" // 场景视图
#include "Settings/LyraSettingsShared.h" // Lyra共享设置

#include UE_INLINE_GENERATED_CPP_BY_NAME(AimAssistInputModifier) // 包含内联生成的cpp文件

#if ENABLE_DRAW_DEBUG // 如果启用绘制调试
#include "Engine/Canvas.h" // 画布
#include "Debug/DebugDrawService.h" // 调试绘制服务
#endif    // ENABLE_DRAW_DEBUG

DEFINE_LOG_CATEGORY(LogAimAssist); // 定义日志类别

namespace LyraConsoleVariables // Lyra控制台变量命名空间
{
    static bool bEnableAimAssist = true; // 静态布尔变量：启用瞄准辅助
    static FAutoConsoleVariableRef CVarEnableAimAssist( // 自动控制台变量引用
        TEXT("lyra.Weapon.EnableAimAssist"), // 控制台变量名称
        bEnableAimAssist, // 变量引用
        TEXT("Should we enable aim assist while shooting?"), // 帮助文本
        ECVF_Cheat); // 控制台变量标志（作弊）

    static bool bDrawAimAssistDebug = false; // 静态布尔变量：绘制瞄准辅助调试
    static FAutoConsoleVariableRef CVarDrawAimAssistDebug( // 自动控制台变量引用
        TEXT("lyra.Weapon.DrawAimAssistDebug"), // 控制台变量名称
        bDrawAimAssistDebug, // 变量引用
        TEXT("Should we draw some debug stats about aim assist?"), // 帮助文本
        ECVF_Cheat); // 控制台变量标志（作弊）
}

///////////////////////////////////////////////////////////////////
// FLyraAimAssistTarget

void FLyraAimAssistTarget::ResetTarget() // 重置目标
{
    TargetShapeComponent = nullptr; // 目标形状组件置空

    Location = FVector::ZeroVector; // 位置置零
    DeltaMovement = FVector::ZeroVector; // 增量移动置零
    ScreenBounds.Init(); // 屏幕边界初始化

    ViewDistance = 0.0f; // 视图距离置零
    SortScore = 0.0f; // 排序分数置零

    AssistTime = 0.0f; // 辅助时间置零
    AssistWeight = 0.0f; // 辅助权重置零

    VisibilityTraceHandle = FTraceHandle(); // 可见性追踪句柄置空

    bIsVisible = false; // 不可见
    bUnderAssistInnerReticle = false; // 不在辅助内准星下
    bUnderAssistOuterReticle = false;    // 不在辅助外准星下
}

FRotator FLyraAimAssistTarget::GetRotationFromMovement(const FAimAssistOwnerViewData& OwnerInfo) const // 从移动获取旋转
{
    ensure(OwnerInfo.IsDataValid()); // 确保拥有者数据有效

    // 将所有内容转换为玩家空间。
    // 在新目标位置中考虑玩家移动。
    const FVector OldLocation = OwnerInfo.PlayerInverseTransform.TransformPositionNoScale(Location - DeltaMovement); // 旧位置：玩家逆变换转换位置
    const FVector NewLocation = OwnerInfo.PlayerInverseTransform.TransformPositionNoScale(Location - OwnerInfo.DeltaMovement); // 新位置：玩家逆变换转换位置

    FRotator RotationToTarget; // 到目标的旋转
    RotationToTarget.Yaw = CalculateRotationToTarget2D(NewLocation.X, NewLocation.Y, OldLocation.Y); // 偏航：计算到目标的2D旋转
    RotationToTarget.Pitch = CalculateRotationToTarget2D(NewLocation.X, NewLocation.Z, OldLocation.Z); // 俯仰：计算到目标的2D旋转
    RotationToTarget.Roll = 0.0f; // 滚转为0

    return RotationToTarget; // 返回到目标的旋转
}

float FLyraAimAssistTarget::CalculateRotationToTarget2D(float TargetX, float TargetY, float OffsetY) const // 计算到目标的2D旋转
{
    if (TargetX <= 0.0f) // 如果目标X小于等于0
    {
        return 0.0f; // 返回0
    }

    const float AngleA = FMath::RadiansToDegrees(FMath::Atan2(TargetY, TargetX)); // 角度A：弧度转角度，计算atan2

    if (FMath::IsNearlyZero(OffsetY)) // 如果偏移Y接近0
    {
        return AngleA; // 返回角度A
    }

    const float Distance = FMath::Sqrt((TargetX * TargetX) + (TargetY * TargetY)); // 距离：计算平方和开方
    ensure(Distance > 0.0f); // 确保距离大于0

    const float AngleB = FMath::RadiansToDegrees(FMath::Asin(OffsetY / Distance)); // 角度B：弧度转角度，计算asin

    return FRotator::NormalizeAxis(AngleA - AngleB); // 返回规范化轴的角度A减角度B
}

///////////////////////////////////////////////////////////////////
// FAimAssistSettings

FAimAssistSettings::FAimAssistSettings() // 构造函数
{
    AssistInnerReticleWidth.SetValue(20.0f); // 辅助内准星宽度设置值
    AssistInnerReticleHeight.SetValue(20.0f); // 辅助内准星高度设置值
    AssistOuterReticleWidth.SetValue(80.0f); // 辅助外准星宽度设置值
    AssistOuterReticleHeight.SetValue(80.0f); // 辅助外准星高度设置值

    TargetingReticleWidth.SetValue(1200.0f); // 瞄准准星宽度设置值
    TargetingReticleHeight.SetValue(675.0f); // 瞄准准星高度设置值
    TargetRange.SetValue(10000.0f); // 目标范围设置值
    TargetWeightCurve = nullptr; // 目标权重曲线置空

    PullInnerStrengthHip.SetValue(0.6f); // 腰射内拉力强度设置值
    PullOuterStrengthHip.SetValue(0.5f); // 腰射外拉力强度设置值
    PullInnerStrengthAds.SetValue(0.7f); // 瞄准内拉力强度设置值
    PullOuterStrengthAds.SetValue(0.4f); // 瞄准外拉力强度设置值
    PullLerpInRate.SetValue(60.0f); // 拉力淡入速率设置值
    PullLerpOutRate.SetValue(4.0f); // 拉力淡出速率设置值
    PullMaxRotationRate.SetValue(0.0f); // 最大旋转速率设置值

    SlowInnerStrengthHip.SetValue(0.6f); // 腰射内减速强度设置值
    SlowOuterStrengthHip.SetValue(0.5f); // 腰射外减速强度设置值
    SlowInnerStrengthAds.SetValue(0.7f); // 瞄准内减速强度设置值
    SlowOuterStrengthAds.SetValue(0.4f); // 瞄准外减速强度设置值
    SlowLerpInRate.SetValue(60.0f); // 减速淡入速率设置值
    SlowLerpOutRate.SetValue(4.0f); // 减速淡出速率设置值
    SlowMinRotationRate.SetValue(0.0f); // 最小旋转速率设置值

    bEnableAsyncVisibilityTrace = true; // 启用异步可见性追踪
    bRequireInput = true; // 要求输入
    bApplyPull = true; // 应用拉力
    bApplySlowing = true; // 应用减速
    bApplyStrafePullScale = true; // 应用平移拉力缩放
    bUseDynamicSlow = true; // 使用动态减速
    bUseRadialLookRates = true; // 使用径向观察速率
}

float FAimAssistSettings::GetTargetWeightForTime(float Time) const // 获取时间对应的目标权重
{
    if (!ensure(TargetWeightCurve != nullptr)) // 如果不确保目标权重曲线不为空
    {
        return 0.0f; // 返回0
    }

    return FMath::Clamp(TargetWeightCurve->GetFloatValue(Time), 0.0f, 1.0f); // 返回钳制在0到1之间的浮点值
}

float FAimAssistSettings::GetTargetWeightMaxTime() const // 获取目标权重最大时间
{
    if (!ensure(TargetWeightCurve != nullptr)) // 如果不确保目标权重曲线不为空
    {
        return 0.0f; // 返回0
    }

    float MinTime = 0.0f; // 最小时间
    float MaxTime = 0.0f; // 最大时间

    TargetWeightCurve->FloatCurve.GetTimeRange(MinTime, MaxTime); // 浮点曲线获取时间范围

    return MaxTime; // 返回最大时间
}

///////////////////////////////////////////////////////////////////
// FAimAssistOwnerViewData

void FAimAssistOwnerViewData::UpdateViewData(const APlayerController* PC) // 更新视图数据
{
    FSceneViewProjectionData ProjectionData; // 场景视图投影数据
    PlayerController = PC; // 玩家控制器
    LocalPlayer = PlayerController ? PlayerController->GetLocalPlayer() : nullptr; // 本地玩家：如果玩家控制器存在则获取本地玩家，否则为空

    if (!IsDataValid() || !PlayerController || !LocalPlayer) // 如果数据无效或玩家控制器为空或本地玩家为空
    {
        ResetViewData(); // 重置视图数据
        return; // 返回
    }
    
    const APawn* Pawn = Cast<APawn>(PlayerController->GetPawn()); // Pawn：转换玩家控制器的pawn
    
    if (!Pawn || !LocalPlayer || !LocalPlayer->ViewportClient || !LocalPlayer->GetProjectionData(LocalPlayer->ViewportClient->Viewport, ProjectionData)) // 如果pawn为空或本地玩家为空或视口客户端为空或无法获取投影数据
    {
        ResetViewData(); // 重置视图数据
        return; // 返回
    }

    FVector ViewLocation; // 视图位置
    FRotator ViewRotation; // 视图旋转
    PC->GetPlayerViewPoint(ViewLocation, ViewRotation); // 获取玩家视点

    ProjectionMatrix = ProjectionData.ProjectionMatrix; // 投影矩阵
    ViewProjectionMatrix = ProjectionData.ComputeViewProjectionMatrix(); // 视图投影矩阵
    ViewRect = ProjectionData.GetConstrainedViewRect(); // 视图矩形
    ViewTransform = FTransform(ViewRotation, ViewLocation); // 视图变换
    ViewForward = ViewTransform.GetUnitAxis(EAxis::X); // 视图前向

    const FVector OldLocation = PlayerTransform.GetTranslation(); // 旧位置：玩家变换的平移
    const FVector NewLocation = Pawn->GetActorLocation(); // 新位置：pawn的actor位置
    const FRotator NewRotation = PC->GetControlRotation(); // 新旋转：控制器的控制旋转

    PlayerTransform = FTransform(NewRotation, NewLocation); // 玩家变换
    PlayerInverseTransform = PlayerTransform.Inverse(); // 玩家逆变换

    DeltaMovement = (NewLocation - OldLocation); // 增量移动

    // 设置队伍ID
    if (ALyraPlayerState* LyraPS = PlayerController->GetPlayerState<ALyraPlayerState>()) // 如果获取到Lyra玩家状态
    {
        TeamID = LyraPS->GetTeamId(); // 队伍ID
    }
    else // 否则
    {
        TeamID = INDEX_NONE; // 队伍ID为INDEX_NONE
    }
}

void FAimAssistOwnerViewData::ResetViewData() // 重置视图数据
{
    PlayerController = nullptr; // 玩家控制器置空
    LocalPlayer = nullptr; // 本地玩家置空
    
    ProjectionMatrix = FMatrix::Identity; // 投影矩阵置为单位矩阵
    ViewProjectionMatrix = FMatrix::Identity; // 视图投影矩阵置为单位矩阵
    ViewRect = FIntRect(0, 0, 0, 0); // 视图矩形置零
    ViewTransform = FTransform::Identity; // 视图变换置为单位变换

    PlayerTransform = FTransform::Identity; // 玩家变换置为单位变换
    PlayerInverseTransform = FTransform::Identity; // 玩家逆变换置为单位变换
    ViewForward = FVector::ZeroVector; // 视图前向置零

    DeltaMovement = FVector::ZeroVector; // 增量移动置零
    TeamID = INDEX_NONE; // 队伍ID为INDEX_NONE
}

FBox2D FAimAssistOwnerViewData::ProjectReticleToScreen(float ReticleWidth, float ReticleHeight, float ReticleDepth) const // 将准星投影到屏幕
{
    FBox2D ReticleBounds(ForceInitToZero); // 准星边界强制初始化为零

    const FVector ReticleExtents((ReticleWidth * 0.5f), -(ReticleHeight * 0.5f), ReticleDepth); // 准星范围

    if (FSceneView::ProjectWorldToScreen(ReticleExtents, ViewRect, ProjectionMatrix, ReticleBounds.Max)) // 如果世界到屏幕投影成功
    {
        ReticleBounds.Min.X = ViewRect.Min.X + (ViewRect.Max.X - ReticleBounds.Max.X); // 最小X
        ReticleBounds.Min.Y = ViewRect.Min.Y + (ViewRect.Max.Y - ReticleBounds.Max.Y); // 最小Y

        ReticleBounds.bIsValid = true; // 设置为有效
    }

    return ReticleBounds; // 返回准星边界
}

FBox2D FAimAssistOwnerViewData::ProjectBoundsToScreen(const FBox& Bounds) const // 将边界框投影到屏幕
{
    FBox2D Box2D(ForceInitToZero); // 2D盒子强制初始化为零

    if (Bounds.IsValid) // 如果边界有效
    {
        const FVector Vertices[] = // 顶点数组
        {
            FVector(Bounds.Min), // 最小点
            FVector(Bounds.Min.X, Bounds.Min.Y, Bounds.Max.Z), // 最小X，最小Y，最大Z
            FVector(Bounds.Min.X, Bounds.Max.Y, Bounds.Min.Z), // 最小X，最大Y，最小Z
            FVector(Bounds.Max.X, Bounds.Min.Y, Bounds.Min.Z), // 最大X，最小Y，最小Z
            FVector(Bounds.Max.X, Bounds.Max.Y, Bounds.Min.Z), // 最大X，最大Y，最小Z
            FVector(Bounds.Max.X, Bounds.Min.Y, Bounds.Max.Z), // 最大X，最小Y，最大Z
            FVector(Bounds.Min.X, Bounds.Max.Y, Bounds.Max.Z), // 最小X，最大Y，最大Z
            FVector(Bounds.Max) // 最大点
        };

        for (int32 VerticeIndex = 0; VerticeIndex < UE_ARRAY_COUNT(Vertices); ++VerticeIndex) // 遍历顶点
        {
            FVector2D ScreenPoint; // 屏幕点
            if (FSceneView::ProjectWorldToScreen(Vertices[VerticeIndex], ViewRect, ViewProjectionMatrix, ScreenPoint)) // 如果世界到屏幕投影成功
            {
                Box2D += ScreenPoint; // 累加屏幕点
            }
        }
    }

    return Box2D; // 返回2D盒子
}

FBox2D FAimAssistOwnerViewData::ProjectShapeToScreen(const FCollisionShape& Shape, const FVector& ShapeOrigin, const FTransform& WorldTransform) const // 将形状投影到屏幕
{
    FBox2D Box2D(ForceInitToZero); // 2D盒子强制初始化为零

    switch (Shape.ShapeType) // 根据形状类型切换
    {
    case ECollisionShape::Box: // 如果是盒子
        Box2D = ProjectBoxToScreen(Shape, ShapeOrigin, WorldTransform); // 调用投影盒子到屏幕
        break; // 跳出
    case ECollisionShape::Sphere: // 如果是球体
        Box2D = ProjectSphereToScreen(Shape, ShapeOrigin, WorldTransform); // 调用投影球体到屏幕
        break; // 跳出
    case ECollisionShape::Capsule: // 如果是胶囊体
        Box2D = ProjectCapsuleToScreen(Shape, ShapeOrigin, WorldTransform); // 调用投影胶囊体到屏幕
        break; // 跳出
    default: // 默认
        UE_LOG(LogAimAssist, Warning, TEXT("FAimAssistOwnerViewData::ProjectShapeToScreen() - Invalid shape type!")); // 日志：无效形状类型
        break; // 跳出
    }

    return Box2D; // 返回2D盒子
}

FBox2D FAimAssistOwnerViewData::ProjectBoxToScreen(const FCollisionShape& Shape, const FVector& ShapeOrigin, const FTransform& WorldTransform) const // 将盒子投影到屏幕
{
    check(Shape.IsBox()); // 检查是盒子
    check(!Shape.IsNearlyZero()); // 检查不是接近零

    const FVector BoxExtents = Shape.GetBox(); // 盒子范围

    const FVector Vertices[] = // 顶点数组
    {
        FVector(-BoxExtents.X, -BoxExtents.Y, -BoxExtents.Z), // 负X，负Y，负Z
        FVector(-BoxExtents.X, -BoxExtents.Y,  BoxExtents.Z), // 负X，负Y，正Z
        FVector(-BoxExtents.X,  BoxExtents.Y, -BoxExtents.Z), // 负X，正Y，负Z
        FVector(-BoxExtents.X,  BoxExtents.Y,  BoxExtents.Z), // 负X，正Y，正Z
        FVector( BoxExtents.X, -BoxExtents.Y, -BoxExtents.Z), // 正X，负Y，负Z
        FVector( BoxExtents.X, -BoxExtents.Y,  BoxExtents.Z), // 正X，负Y，正Z
        FVector( BoxExtents.X,  BoxExtents.Y, -BoxExtents.Z), // 正X，正Y，负Z
        FVector( BoxExtents.X,  BoxExtents.Y,  BoxExtents.Z) // 正X，正Y，正Z
    };

    FBox2D Box2D(ForceInitToZero); // 2D盒子强制初始化为零

    for (int32 VerticeIndex = 0; VerticeIndex < UE_ARRAY_COUNT(Vertices); ++VerticeIndex) // 遍历顶点
    {
        const FVector Vertex = WorldTransform.TransformPositionNoScale(Vertices[VerticeIndex] + ShapeOrigin); // 顶点：世界变换转换位置

        FVector2D ScreenPoint; // 屏幕点
        if (FSceneView::ProjectWorldToScreen(Vertex, ViewRect, ViewProjectionMatrix, ScreenPoint)) // 如果世界到屏幕投影成功
        {
            Box2D += ScreenPoint; // 累加屏幕点
        }
    }

    return Box2D; // 返回2D盒子
}

FBox2D FAimAssistOwnerViewData::ProjectSphereToScreen(const FCollisionShape& Shape, const FVector& ShapeOrigin, const FTransform& WorldTransform) const // 将球体投影到屏幕
{
    check(Shape.IsSphere()); // 检查是球体
    check(!Shape.IsNearlyZero()); // 检查不是接近零

    const FVector ViewAxisY = ViewTransform.GetUnitAxis(EAxis::Y); // 视图Y轴
    const FVector ViewAxisZ = ViewTransform.GetUnitAxis(EAxis::Z); // 视图Z轴

    const float SphereRadius = Shape.GetSphereRadius(); // 球体半径
    const FVector SphereLocation = WorldTransform.TransformPositionNoScale(ShapeOrigin); // 球体位置：世界变换转换位置
    const FVector SphereExtent = (ViewAxisY * SphereRadius) + (ViewAxisZ * SphereRadius); // 球体范围

    const FVector Vertices[] = // 顶点数组
    {
        FVector(SphereLocation + SphereExtent), // 球体位置加范围
        FVector(SphereLocation - SphereExtent), // 球体位置减范围
    };

    FBox2D Box2D(ForceInitToZero); // 2D盒子强制初始化为零

    for (int32 VerticeIndex = 0; VerticeIndex < UE_ARRAY_COUNT(Vertices); ++VerticeIndex) // 遍历顶点
    {
        FVector2D ScreenPoint; // 屏幕点
        if (FSceneView::ProjectWorldToScreen(Vertices[VerticeIndex], ViewRect, ViewProjectionMatrix, ScreenPoint)) // 如果世界到屏幕投影成功
        {
            Box2D += ScreenPoint; // 累加屏幕点
        }
    }

    return Box2D; // 返回2D盒子
}

FBox2D FAimAssistOwnerViewData::ProjectCapsuleToScreen(const FCollisionShape& Shape, const FVector& ShapeOrigin, const FTransform& WorldTransform) const // 将胶囊体投影到屏幕
{
    check(Shape.IsCapsule()); // 检查是胶囊体
    check(!Shape.IsNearlyZero()); // 检查不是接近零

    const FVector ViewAxisY = ViewTransform.GetUnitAxis(EAxis::Y); // 视图Y轴
    const FVector ViewAxisZ = ViewTransform.GetUnitAxis(EAxis::Z); // 视图Z轴

    const float CapsuleAxisHalfLength = Shape.GetCapsuleAxisHalfLength(); // 胶囊体轴半长
    const float CapsuleRadius = Shape.GetCapsuleRadius(); // 胶囊体半径

    const FVector TopSphereLocation = WorldTransform.TransformPositionNoScale(FVector(0.0f, 0.0f, CapsuleAxisHalfLength) + ShapeOrigin); // 顶部球体位置
    const FVector BottomSphereLocation = WorldTransform.TransformPositionNoScale(FVector(0.0f, 0.0f, -CapsuleAxisHalfLength) + ShapeOrigin); // 底部球体位置
    const FVector SphereExtent = (ViewAxisY * CapsuleRadius) + (ViewAxisZ * CapsuleRadius); // 球体范围

    const FVector Vertices[] = // 顶点数组
    {
        FVector(TopSphereLocation + SphereExtent), // 顶部球体位置加范围
        FVector(TopSphereLocation - SphereExtent), // 顶部球体位置减范围
        FVector(BottomSphereLocation + SphereExtent), // 底部球体位置加范围
        FVector(BottomSphereLocation - SphereExtent), // 底部球体位置减范围
    };

    FBox2D Box2D(ForceInitToZero); // 2D盒子强制初始化为零

    for (int32 VerticeIndex = 0; VerticeIndex < UE_ARRAY_COUNT(Vertices); ++VerticeIndex) // 遍历顶点
    {
        FVector2D ScreenPoint; // 屏幕点
        if (FSceneView::ProjectWorldToScreen(Vertices[VerticeIndex], ViewRect, ViewProjectionMatrix, ScreenPoint)) // 如果世界到屏幕投影成功
        {
            Box2D += ScreenPoint; // 累加屏幕点
        }
    }

    return Box2D; // 返回2D盒子
}

///////////////////////////////////////////////////////////////////
// UAimAssistInputModifier

static const float GamepadUserOptions_YawLookRateBase = 900.0f; // 静态常量：游戏手柄用户选项偏航观察速率基准
static const float GamepadUserOptions_PitchLookRateBase = (GamepadUserOptions_YawLookRateBase * 0.6f); // 静态常量：游戏手柄用户选项俯仰观察速率基准

// TODO 将其设为constexpr而不是define
#define YawLookSpeedToRotationRate(_Speed)        ((_Speed) / 100.0f * GamepadUserOptions_YawLookRateBase) // 宏：偏航观察速度转旋转速率
#define PitchLookSpeedToRotationRate(_Speed)    ((_Speed) / 100.0f * GamepadUserOptions_PitchLookRateBase) // 宏：俯仰观察速度转旋转速率

FRotator UAimAssistInputModifier::GetLookRates(const FVector& LookInput) // 获取观察速率
{
    FRotator LookRates; // 观察速率

    const float SensitivityHipLevel = 50.0f; // 灵敏度腰射等级
    {
        LookRates.Yaw = YawLookSpeedToRotationRate(SensitivityHipLevel); // 偏航：偏航观察速度转旋转速率
        LookRates.Pitch = PitchLookSpeedToRotationRate(SensitivityHipLevel); // 俯仰：俯仰观察速度转旋转速率
        LookRates.Roll = 0.0f; // 滚转为0
    }

    LookRates.Yaw = FMath::Clamp(LookRates.Yaw, 0.0f, GamepadUserOptions_YawLookRateBase); // 偏航：钳制在0到基准之间
    LookRates.Pitch = FMath::Clamp(LookRates.Pitch, 0.0f, GamepadUserOptions_PitchLookRateBase); // 俯仰：钳制在0到基准之间

    if (Settings.bUseRadialLookRates) // 如果设置使用径向观察速率
    {
        // 基于摇杆偏转在偏航和俯仰之间混合。这保持了对角线的准确性。
        const float RadialLerp = FMath::Atan2(FMath::Abs(LookInput.Y), FMath::Abs(LookInput.X)) / HALF_PI; // 径向插值：atan2绝对值除以π/2
        const float RadialLookRate = FMath::Lerp(LookRates.Yaw, LookRates.Pitch, RadialLerp); // 径向观察速率：线性插值

        LookRates.Yaw = RadialLookRate; // 偏航设为径向观察速率
        LookRates.Pitch = RadialLookRate;    // 俯仰设为径向观察速率
    }
    
    return LookRates; // 返回观察速率
}

FInputActionValue UAimAssistInputModifier::ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime) // 修改原始输入的实现
{
    TRACE_CPUPROFILER_EVENT_SCOPE(UAimAssistInputModifier::ModifyRaw_Implementation); // CPU性能分析器事件范围

#if ENABLE_DRAW_DEBUG // 如果启用绘制调试
    if (LyraConsoleVariables::bDrawAimAssistDebug) // 如果绘制瞄准辅助调试
    {
        if (!DebugDrawHandle.IsValid()) // 如果调试绘制句柄无效
        {
            DebugDrawHandle = UDebugDrawService::Register(TEXT("Game"), FDebugDrawDelegate::CreateUObject(this, &UAimAssistInputModifier::AimAssistDebugDraw)); // 注册调试绘制服务
        }
        else // 否则
        {
            UDebugDrawService::Unregister(DebugDrawHandle); // 注销调试绘制服务
            DebugDrawHandle.Reset(); // 重置调试绘制句柄
        }
        bRegisteredDebug = true; // 已注册调试设为true
    }
#endif

#if !UE_BUILD_SHIPPING // 如果不是发布版本
    if (!LyraConsoleVariables::bEnableAimAssist) // 如果不启用瞄准辅助
    {
        return CurrentValue; // 返回当前值
    }
#endif //UE_BUILD_SHIPPING

    APlayerController* PC = PlayerInput ? Cast<APlayerController>(PlayerInput->GetOuter()) : nullptr; // 玩家控制器：如果玩家输入存在则转换外部对象，否则为空
    if (!PC) // 如果玩家控制器为空
    {
        return CurrentValue; // 返回当前值
    }

    // 基于当前玩家控制器更新"拥有者"信息。这会计算并存储用于确定哪些目标可见的信息，
    // 例如视图矩阵和当前旋转
    OwnerViewData.UpdateViewData(PC); // 更新拥有者视图数据

    if (!OwnerViewData.IsDataValid()) // 如果拥有者数据无效
    {
        return CurrentValue; // 返回当前值
    }
    
    // 交换目标缓存并确定当前哪些目标可见。更新每个目标的分数以确定
    // 应对每个目标应用多少拉力/减速效果
    UpdateTargetData(DeltaTime); // 更新目标数据

    FVector BaselineInput = CurrentValue.Get<FVector>(); // 基准输入：获取向量值
    
    FVector OutAssistedInput = BaselineInput; // 输出辅助输入设为基准输入
    FVector CurrentMoveInput = MoveInputAction ? PlayerInput->GetActionValue(MoveInputAction).Get<FVector>() : FVector::ZeroVector;    // 当前移动输入：如果移动输入动作存在则获取动作值，否则为零向量

    // 观察速率有些问题
    FRotator LookRates = GetLookRates(BaselineInput); // 获取观察速率
    
    const FRotator RotationalVelocity = UpdateRotationalVelocity(PC, DeltaTime, BaselineInput, CurrentMoveInput); // 旋转速度：更新旋转速度
    
    if (LookRates.Yaw > 0.0f) // 如果偏航观察速率大于0
    {
        OutAssistedInput.X = (RotationalVelocity.Yaw / LookRates.Yaw); // 输出辅助输入X：旋转速度偏航除以观察速率偏航
        OutAssistedInput.X = FMath::Clamp(OutAssistedInput.X, -1.0f, 1.0f); // 钳制在-1到1之间
    }
    
    if (LookRates.Pitch > 0.0f) // 如果俯仰观察速率大于0
    {
        OutAssistedInput.Y = (RotationalVelocity.Pitch / LookRates.Pitch); // 输出辅助输入Y：旋转速度俯仰除以观察速率俯仰
        OutAssistedInput.Y = FMath::Clamp(OutAssistedInput.Y, -1.0f, 1.0f); // 钳制在-1到1之间
    }

#if ENABLE_DRAW_DEBUG // 如果启用绘制调试
    LastBaselineValue = BaselineInput; // 上一帧基准值
    LastLookRatePitch = LookRates.Pitch; // 上一帧俯仰观察速率
    LastLookRateYaw = LookRates.Yaw; // 上一帧偏航观察速率
    LastOutValue = OutAssistedInput; // 上一帧输出值
#endif
    return OutAssistedInput; // 返回输出辅助输入
}

void UAimAssistInputModifier::UpdateTargetData(float DeltaTime) // 更新目标数据
{
    if(!ensure(OwnerViewData.PlayerController)) // 如果不确保拥有者视图数据的玩家控制器存在
    {
        UE_LOG(LogAimAssist, Error, TEXT("[UAimAssistInputModifier::UpdateTargetData] Invalid player controller in owner view data!")); // 日志：拥有者视图数据中玩家控制器无效
        return; // 返回
    }
    
    UAimAssistTargetManagerComponent* TargetManager = nullptr; // 目标管理器置空

    if (UWorld* World = OwnerViewData.PlayerController->GetWorld()) // 如果获取到世界
    {
        if (AGameStateBase* GameState = World->GetGameState()) // 如果获取到游戏状态
        {
            TargetManager = GameState->FindComponentByClass<UAimAssistTargetManagerComponent>();    // 目标管理器：按类查找组件
        }
    }
    
    if (!TargetManager) // 如果目标管理器为空
    {
        return; // 返回
    }

    // 基于可见内容更新目标
    SwapTargetCaches(); // 交换目标缓存
    const TArray<FLyraAimAssistTarget>& OldTargetCache = GetPreviousTargetCache(); // 旧目标缓存：获取上一帧目标缓存
    TArray<FLyraAimAssistTarget>& NewTargetCache = GetCurrentTargetCache(); // 新目标缓存：获取当前帧目标缓存
    
    TargetManager->GetVisibleTargets(Filter, Settings, OwnerViewData, OldTargetCache, NewTargetCache); // 获取可见目标

    //
    // 更新目标权重。
    //
    float TotalAssistWeight = 0.0f; // 总辅助权重

    for (FLyraAimAssistTarget& Target : NewTargetCache) // 遍历新目标缓存
    {
        if (Target.bUnderAssistOuterReticle && Target.bIsVisible) // 如果在辅助外准星下且可见
        {
            const float MaxAssistTime = Settings.GetTargetWeightMaxTime(); // 最大辅助时间：获取目标权重最大时间
            Target.AssistTime = FMath::Min((Target.AssistTime + DeltaTime), MaxAssistTime); // 辅助时间：取最小值
        }
        else // 否则
        {
            Target.AssistTime = FMath::Max((Target.AssistTime - DeltaTime), 0.0f); // 辅助时间：取最大值
        }

        // 基于目标在辅助准星下的时间查找辅助权重。
        Target.AssistWeight = Settings.GetTargetWeightForTime(Target.AssistTime); // 辅助权重：获取时间对应的目标权重

        TotalAssistWeight += Target.AssistWeight; // 累加总辅助权重
    }

    // 归一化权重。
    if (TotalAssistWeight > 0.0f) // 如果总辅助权重大于0
    {
        for (FLyraAimAssistTarget& Target : NewTargetCache) // 遍历新目标缓存
        {
            Target.AssistWeight = (Target.AssistWeight / TotalAssistWeight); // 辅助权重除以总辅助权重
        }
    }
}

const float UAimAssistInputModifier::GetSensitivtyScalar(const ULyraSettingsShared* SharedSettings) const // 获取灵敏度标量
{
    if (SharedSettings && SensitivityLevelTable) // 如果共享设置和灵敏度等级表存在
    {
        const ELyraGamepadSensitivity Sens = TargetingType == ELyraTargetingType::Normal ? SharedSettings->GetGamepadLookSensitivityPreset() : SharedSettings->GetGamepadTargetingSensitivityPreset(); // 灵敏度：根据瞄准类型选择
        return SensitivityLevelTable->SensitivtyEnumToFloat(Sens); // 返回灵敏度枚举转浮点
    }
    
    UE_LOG(LogAimAssist, Warning, TEXT("SensitivityLevelTable is null, using default value!")); // 日志：灵敏度等级表为空，使用默认值
    return (TargetingType == ELyraTargetingType::Normal) ? 1.0f : 0.5f;    // 返回默认值
}

FRotator UAimAssistInputModifier::UpdateRotationalVelocity(APlayerController* PC, float DeltaTime, FVector CurrentLookInputValue, FVector CurrentMoveInputValue) // 更新旋转速度
{
    FRotator RotationalVelocity(ForceInitToZero); // 旋转速度强制初始化为零
    FRotator RotationNeeded(ForceInitToZero); // 所需旋转强制初始化为零
    
    float PullStrength = 0.0f; // 拉力强度
    float SlowStrength = 0.0f; // 减速强度
    
    const TArray<FLyraAimAssistTarget>& TargetCache = GetCurrentTargetCache(); // 目标缓存：获取当前帧目标缓存

    float LookStickDeadzone = 0.25f; // 观察摇杆死区
    float MoveStickDeadzone = 0.25f; // 移动摇杆死区
    float SettingStrengthScalar = (TargetingType == ELyraTargetingType::Normal) ? 1.0f : 0.5f; // 设置强度标量：根据瞄准类型选择

    if (ULyraLocalPlayer* LP = Cast<ULyraLocalPlayer>(PC->GetLocalPlayer())) // 如果获取到Lyra本地玩家
    {
        ULyraSettingsShared* SharedSettings = LP->GetSharedSettings(); // 共享设置：获取共享设置
        LookStickDeadzone = SharedSettings->GetGamepadLookStickDeadZone(); // 观察摇杆死区：获取游戏手柄观察摇杆死区
        MoveStickDeadzone = SharedSettings->GetGamepadMoveStickDeadZone(); // 移动摇杆死区：获取游戏手柄移动摇杆死区
        SettingStrengthScalar = GetSensitivtyScalar(SharedSettings); // 设置强度标量：获取灵敏度标量
    }
    
    for (const FLyraAimAssistTarget& Target : TargetCache) // 遍历目标缓存
    {
        if (Target.bUnderAssistOuterReticle && Target.bIsVisible) // 如果在辅助外准星下且可见
        {
            // 基于目标和玩家移动累加跟随加权目标所需的总旋转。
            RotationNeeded += (Target.GetRotationFromMovement(OwnerViewData) * Target.AssistWeight); // 所需旋转累加

            float TargetPullStrength = 0.0f; // 目标拉力强度
            float TargetSlowStrength = 0.0f; // 目标减速强度
            CalculateTargetStrengths(Target, TargetPullStrength, TargetSlowStrength); // 计算目标强度

            // 累加目标的加权拉力和减速总量。
            PullStrength += TargetPullStrength; // 拉力强度累加
            SlowStrength += TargetSlowStrength; // 减速强度累加
        }
    }

    // 您也可以基于当前装备的武器、玩家的移动状态或任何其他因素在此处应用一些标量
    PullStrength *= Settings.StrengthScale * SettingStrengthScalar; // 拉力强度乘以强度缩放和设置强度标量
    SlowStrength *= Settings.StrengthScale * SettingStrengthScalar; // 减速强度乘以强度缩放和设置强度标量

    const float PullLerpRate = (PullStrength > LastPullStrength) ? Settings.PullLerpInRate.GetValue() : Settings.PullLerpOutRate.GetValue(); // 拉力插值速率：根据拉力强度选择淡入或淡出速率
    if (PullLerpRate > 0.0f) // 如果拉力插值速率大于0
    {
        PullStrength = FMath::FInterpConstantTo(LastPullStrength, PullStrength, DeltaTime, PullLerpRate); // 拉力强度：恒定插值到
    }

    const float SlowLerpRate = (SlowStrength > LastSlowStrength) ? Settings.SlowLerpInRate.GetValue() : Settings.SlowLerpOutRate.GetValue(); // 减速插值速率：根据减速强度选择淡入或淡出速率
    if (SlowLerpRate > 0.0f) // 如果减速插值速率大于0
    {
        SlowStrength = FMath::FInterpConstantTo(LastSlowStrength, SlowStrength, DeltaTime, SlowLerpRate); // 减速强度：恒定插值到
    }

    LastPullStrength = PullStrength; // 上一帧拉力强度
    LastSlowStrength = SlowStrength; // 上一帧减速强度

    const bool bIsLookInputActive =  (CurrentLookInputValue.SizeSquared() > FMath::Square(LookStickDeadzone)); // 是否观察输入活动：大小平方大于死区平方
    const bool bIsMoveInputActive = (CurrentMoveInputValue.SizeSquared() > FMath::Square(MoveStickDeadzone)); // 是否移动输入活动：大小平方大于死区平方
    
    const bool bIsApplyingLookInput = (bIsLookInputActive || !Settings.bRequireInput); // 是否应用观察输入：观察输入活动或不要求输入
    const bool bIsApplyingMoveInput = (bIsMoveInputActive || !Settings.bRequireInput); // 是否应用移动输入：移动输入活动或不要求输入
    const bool bIsApplyingAnyInput = (bIsApplyingLookInput || bIsApplyingMoveInput); // 是否应用任何输入

    // 应用向目标的拉力
    if (Settings.bApplyPull && bIsApplyingAnyInput && !FMath::IsNearlyZero(PullStrength)) // 如果应用拉力且应用任何输入且拉力强度不接近零
    {
        // 拉力量是保持在目标上所需旋转的百分比。
        FRotator PullRotation = (RotationNeeded * PullStrength); // 拉力旋转：所需旋转乘以拉力强度

        if (!bIsApplyingLookInput && Settings.bApplyStrafePullScale) // 如果不应用观察输入且应用平移拉力缩放
        {
            // 如果玩家没有主动观察周围，则按玩家平移量缩放拉力强度。
            // 这有助于防止在向前跑过目标时视角猛拉。
            float StrafePullScale = FMath::Abs(CurrentMoveInputValue.Y); // 平移拉力缩放：移动输入值Y的绝对值
        
            PullRotation.Yaw *= StrafePullScale; // 偏航乘以平移拉力缩放
            PullRotation.Pitch *= StrafePullScale; // 俯仰乘以平移拉力缩放
        }

        // 钳制最大拉力量以防止过度猛拉玩家的视角。
        // 钳制的速率经过缩放，因此无论视野如何感觉都相同。
        const float FOVScale = UAimAssistTargetManagerComponent::GetFOVScale(PC, ECommonInputType::Gamepad); // 视野缩放：获取视野缩放
        const float PullMaxRotationRate = (Settings.PullMaxRotationRate.GetValue() * FOVScale); // 最大旋转速率：设置值乘以视野缩放
        if (PullMaxRotationRate > 0.0f) // 如果最大旋转速率大于0
        {
            const float PullMaxRotation = (PullMaxRotationRate * DeltaTime); // 最大旋转：最大旋转速率乘以DeltaTime

            PullRotation.Yaw = FMath::Clamp(PullRotation.Yaw, -PullMaxRotation, PullMaxRotation); // 偏航钳制
            PullRotation.Pitch = FMath::Clamp(PullRotation.Pitch, -PullMaxRotation, PullMaxRotation); // 俯仰钳制
        }

        RotationNeeded -= PullRotation; // 所需旋转减去拉力旋转
        RotationalVelocity += (PullRotation * (1.0f / DeltaTime)); // 旋转速度累加
    }

    FRotator LookRates = GetLookRates(CurrentLookInputValue); // 观察速率：获取观察速率

    // 应用减速
    if (Settings.bApplySlowing && bIsApplyingLookInput && !FMath::IsNearlyZero(SlowStrength)) // 如果应用减速且应用观察输入且减速强度不接近零
    {
        // 减速的旋转速率是正常观察旋转速率的百分比。
        FRotator SlowRates = (LookRates * (1.0f - SlowStrength)); // 减速速率：观察速率乘以(1-减速强度)

        const bool bUseDynamicSlow = true; // 使用动态减速

        if (Settings.bUseDynamicSlow) // 如果设置使用动态减速
        {
            const FRotator BoostRotation = (RotationNeeded * (1.0f / DeltaTime)); // 增强旋转：所需旋转除以DeltaTime

            const float YawDynamicBoost = (BoostRotation.Yaw * FMath::Sign(CurrentLookInputValue.X)); // 偏航动态增强：增强旋转偏航乘以观察输入值X的符号
            if (YawDynamicBoost > 0.0f) // 如果偏航动态增强大于0
            {
                SlowRates.Yaw += YawDynamicBoost; // 减速速率偏航累加
            }

            const float PitchDynamicBoost = (BoostRotation.Pitch * FMath::Sign(CurrentLookInputValue.Y)); // 俯仰动态增强：增强旋转俯仰乘以观察输入值Y的符号
            if (PitchDynamicBoost > 0.0f) // 如果俯仰动态增强大于0
            {
                SlowRates.Pitch += PitchDynamicBoost; // 减速速率俯仰累加
            }
        }

        // 钳制最小减速度以防止在低灵敏度设置下感觉迟钝。
        // 钳制的速率经过缩放，因此无论视野如何感觉都相同。
        const float FOVScale = UAimAssistTargetManagerComponent::GetFOVScale(PC, ECommonInputType::Gamepad); // 视野缩放：获取视野缩放
        const float SlowMinRotationRate = (Settings.SlowMinRotationRate.GetValue() * FOVScale); // 最小旋转速率：设置值乘以视野缩放
        if (SlowMinRotationRate > 0.0f) // 如果最小旋转速率大于0
        {
            SlowRates.Yaw = FMath::Max(SlowRates.Yaw, SlowMinRotationRate); // 减速速率偏航取最大值
            SlowRates.Pitch = FMath::Max(SlowRates.Pitch, SlowMinRotationRate); // 减速速率俯仰取最大值
        }

        // 确保减速速率不超过默认值。
        SlowRates.Yaw = FMath::Min(SlowRates.Yaw, LookRates.Yaw); // 减速速率偏航取最小值
        SlowRates.Pitch = FMath::Min(SlowRates.Pitch, LookRates.Pitch); // 减速速率俯仰取最小值

        RotationalVelocity.Yaw += (CurrentLookInputValue.X * SlowRates.Yaw); // 旋转速度偏航累加
        RotationalVelocity.Pitch += (CurrentLookInputValue.Y * SlowRates.Pitch); // 旋转速度俯仰累加
        RotationalVelocity.Roll = 0.0f; // 滚转为0
    }
    else // 否则
    {
        RotationalVelocity.Yaw += (CurrentLookInputValue.X * LookRates.Yaw); // 旋转速度偏航累加
        RotationalVelocity.Pitch += (CurrentLookInputValue.Y * LookRates.Pitch); // 旋转速度俯仰累加
        RotationalVelocity.Roll = 0.0f; // 滚转为0
    }

    return RotationalVelocity; // 返回旋转速度
}

void UAimAssistInputModifier::CalculateTargetStrengths(const FLyraAimAssistTarget& Target, float& OutPullStrength, float& OutSlowStrength) const // 计算目标强度
{
    const bool bIsADS = (TargetingType == ELyraTargetingType::ADS); // 是否瞄准：瞄准类型为ADS
    
    if (Target.bUnderAssistInnerReticle) // 如果在辅助内准星下
    {
        if (bIsADS) // 如果瞄准
        {
            OutPullStrength = Settings.PullInnerStrengthAds.GetValue(); // 输出拉力强度：瞄准内拉力强度设置值
            OutSlowStrength = Settings.SlowInnerStrengthAds.GetValue(); // 输出减速强度：瞄准内减速强度设置值
        }
        else // 否则
        {
            OutPullStrength = Settings.PullInnerStrengthHip.GetValue(); // 输出拉力强度：腰射内拉力强度设置值
            OutSlowStrength = Settings.SlowInnerStrengthHip.GetValue(); // 输出减速强度：腰射内减速强度设置值
        }
    }
    else if (Target.bUnderAssistOuterReticle) // 如果在辅助外准星下
    {
        if (bIsADS) // 如果瞄准
        {
            OutPullStrength = Settings.PullOuterStrengthAds.GetValue(); // 输出拉力强度：瞄准外拉力强度设置值
            OutSlowStrength = Settings.SlowOuterStrengthAds.GetValue(); // 输出减速强度：瞄准外减速强度设置值
        }
        else // 否则
        {
            OutPullStrength = Settings.PullOuterStrengthHip.GetValue(); // 输出拉力强度：腰射外拉力强度设置值
            OutSlowStrength = Settings.SlowOuterStrengthHip.GetValue(); // 输出减速强度：腰射外减速强度设置值
        }
    }
    else // 否则
    {
        OutPullStrength = 0.0f; // 输出拉力强度为0
        OutSlowStrength = 0.0f; // 输出减速强度为0
    }

    OutPullStrength *= Target.AssistWeight; // 输出拉力强度乘以辅助权重
    OutSlowStrength *= Target.AssistWeight; // 输出减速强度乘以辅助权重
}

#if ENABLE_DRAW_DEBUG // 如果启用绘制调试
void UAimAssistInputModifier::AimAssistDebugDraw(UCanvas* Canvas, APlayerController* PC) // 瞄准辅助调试绘制
{
    if (!Canvas || !OwnerViewData.IsDataValid() || !LyraConsoleVariables::bDrawAimAssistDebug) // 如果画布为空或拥有者数据无效或不绘制瞄准辅助调试
    {
        return; // 返回
    }

    const bool bIsADS = (TargetingType == ELyraTargetingType::ADS); // 是否瞄准：瞄准类型为ADS
    
    FDisplayDebugManager& DisplayDebugManager = Canvas->DisplayDebugManager; // 显示调试管理器引用
    DisplayDebugManager.Initialize(Canvas, GEngine->GetSmallFont(), FVector2D((bIsADS ? 4.0f : 170.0f), 150.0f)); // 初始化显示调试管理器
    DisplayDebugManager.SetDrawColor(FColor::Yellow); // 设置绘制颜色为黄色

    DisplayDebugManager.DrawString(FString(TEXT("------------------------------"))); // 绘制字符串
    DisplayDebugManager.DrawString(FString(TEXT("Aim Assist Debug Draw"))); // 绘制字符串
    DisplayDebugManager.DrawString(FString(TEXT("------------------------------"))); // 绘制字符串
    DisplayDebugManager.DrawString(FString::Printf(TEXT("Strength Scale: (%.4f)"), Settings.StrengthScale)); // 绘制字符串：强度缩放
    DisplayDebugManager.DrawString(FString::Printf(TEXT("Pull Strength: (%.4f)"), LastPullStrength)); // 绘制字符串：拉力强度
    DisplayDebugManager.DrawString(FString::Printf(TEXT("Slow Strength: (%.4f)"), LastSlowStrength)); // 绘制字符串：减速强度
    DisplayDebugManager.DrawString(FString::Printf(TEXT("Look Rate Yaw: (%.4f)"), LastLookRateYaw)); // 绘制字符串：偏航观察速率
    DisplayDebugManager.DrawString(FString::Printf(TEXT("Look Rate Pitch: (%.4f)"), LastLookRatePitch)); // 绘制字符串：俯仰观察速率
    DisplayDebugManager.DrawString(FString::Printf(TEXT("Baseline Value: (%.4f, %.4f, %.4f)"), LastBaselineValue.X, LastBaselineValue.Y, LastBaselineValue.Z)); // 绘制字符串：基准值
    DisplayDebugManager.DrawString(FString::Printf(TEXT("Assisted Value: (%.4f, %.4f, %.4f)"), LastOutValue.X, LastOutValue.Y, LastOutValue.Z)); // 绘制字符串：辅助值

    
    UWorld* World = OwnerViewData.PlayerController->GetWorld(); // 世界：获取世界
    check(World); // 检查世界

    const FBox2D AssistInnerReticleBounds = OwnerViewData.ProjectReticleToScreen(Settings.AssistInnerReticleWidth.GetValue(), Settings.AssistInnerReticleHeight.GetValue(), Settings.ReticleDepth); // 辅助内准星边界：投影准星到屏幕
    const FBox2D AssistOuterReticleBounds = OwnerViewData.ProjectReticleToScreen(Settings.AssistOuterReticleWidth.GetValue(), Settings.AssistOuterReticleHeight.GetValue(), Settings.ReticleDepth); // 辅助外准星边界：投影准星到屏幕
    const FBox2D TargetingReticleBounds = OwnerViewData.ProjectReticleToScreen(Settings.TargetingReticleWidth.GetValue(), Settings.TargetingReticleHeight.GetValue(), Settings.ReticleDepth); // 瞄准准星边界：投影准星到屏幕

    if (TargetingReticleBounds.bIsValid) // 如果瞄准准星边界有效
    {
        FLinearColor ReticleColor(0.25f, 0.25f, 0.25f, 1.0f); // 准星颜色
        DrawDebugCanvas2DBox(Canvas, TargetingReticleBounds, ReticleColor, 1.0f);    // 绘制调试画布2D盒子
    }

    if (AssistInnerReticleBounds.bIsValid) // 如果辅助内准星边界有效
    {
        FLinearColor ReticleColor(0.0f, 0.0f, 1.0f, 0.2f); // 准星颜色

        FCanvasTileItem ReticleTileItem(AssistInnerReticleBounds.Min, AssistInnerReticleBounds.GetSize(), ReticleColor); // 画布平铺项
        ReticleTileItem.BlendMode = SE_BLEND_Translucent; // 混合模式为半透明
        Canvas->DrawItem(ReticleTileItem); // 绘制项

        ReticleColor.A = 1.0f; // 透明度设为1
        DrawDebugCanvas2DBox(Canvas, AssistInnerReticleBounds, ReticleColor, 1.0f); // 绘制调试画布2D盒子
    }

    if (AssistOuterReticleBounds.bIsValid) // 如果辅助外准星边界有效
    {
        FLinearColor ReticleColor(0.25f, 0.25f, 1.0f, 0.2f); // 准星颜色

        FCanvasTileItem ReticleTileItem(AssistOuterReticleBounds.Min, AssistOuterReticleBounds.GetSize(), ReticleColor); // 画布平铺项
        ReticleTileItem.BlendMode = SE_BLEND_Translucent; // 混合模式为半透明
        Canvas->DrawItem(ReticleTileItem); // 绘制项

        ReticleColor.A = 1.0f; // 透明度设为1
        DrawDebugCanvas2DBox(Canvas, AssistOuterReticleBounds, ReticleColor, 1.0f); // 绘制调试画布2D盒子
    }

    const TArray<FLyraAimAssistTarget>& TargetCache = GetCurrentTargetCache(); // 目标缓存：获取当前帧目标缓存
    for (const FLyraAimAssistTarget& Target : TargetCache) // 遍历目标缓存
    {
        if (Target.ScreenBounds.bIsValid) // 如果屏幕边界有效
        {
            FLinearColor TargetColor = ((Target.AssistWeight > 0.0f) ? FLinearColor::LerpUsingHSV(FLinearColor::Yellow, FLinearColor::Green, Target.AssistWeight) : FLinearColor::Black); // 目标颜色：根据辅助权重插值
            TargetColor.A = 0.2f; // 透明度设为0.2

            FCanvasTileItem TargetTileItem(Target.ScreenBounds.Min, Target.ScreenBounds.GetSize(), TargetColor); // 画布平铺项
            TargetTileItem.BlendMode = SE_BLEND_Translucent; // 混合模式为半透明
            Canvas->DrawItem(TargetTileItem); // 绘制项

            if (Target.bIsVisible) // 如果可见
            {
                TargetColor.A = 1.0f; // 透明度设为1
                DrawDebugCanvas2DBox(Canvas, Target.ScreenBounds, TargetColor, 1.0f); // 绘制调试画布2D盒子
            }

            FCanvasTextItem TargetTextItem(FVector2D::ZeroVector, FText::FromString(FString::Printf(TEXT("Weight: %.2f\nDist: %.2f\nScore: %.2f\nTime: %.2f"), Target.AssistWeight, Target.ViewDistance, Target.SortScore, Target.AssistTime)), GEngine->GetSmallFont(), FLinearColor::White); // 画布文本项
            TargetTextItem.EnableShadow(FLinearColor::Black); // 启用阴影
            Canvas->DrawItem(TargetTextItem, FVector2D(FMath::CeilToFloat(Target.ScreenBounds.Min.X), FMath::CeilToFloat(Target.ScreenBounds.Min.Y))); // 绘制项
        }
    }
}
#endif    // ENABLE_DRAW_DEBUG